package org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util;

import java.util.ArrayList;

import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.futbolista.FutbolistaBase;


/**
 * @author Joan Crosas
 *
 */
public class Balon {
	private final int POSICIONES_ANTERIORES_MAX = 2;

	private ArrayList<PosicionBalon> posicionBalon;
	private ArrayList<PosicionBalon> cache;
	
	private PosicionBalon posicionRebote;
	private PosicionBalon posicionEnLineaFondo;

	public Balon() {
		posicionBalon = new ArrayList<PosicionBalon>();
		cache = new ArrayList<PosicionBalon>();
	}
	
	
	/**
	 * @param posicionInicial
	 * @param angulo
	 * @param velocidadInicial velocidadInicial = fuerzaDisparo * velocidadDisparoJugador;
	 * @param anguloVertical
	 */
	public Balon( PosicionCampo posicionInicial, double angulo, double velocidadInicial, double anguloVertical ) {
		this();
		
		posicionBalon.add( new PosicionBalon( posicionInicial, 0.0 ) );
		
		double altura;
		double dz;
		
		angulo = corregirAngulo( angulo + Math.PI );
		
		if( velocidadInicial > 0 ) {
			altura = 0.0;
			
			// realitzem les operacions inverses per obtenir un punt anterior
			velocidadInicial = velocidadInicial * Math.cos( anguloVertical );
			if( anguloVertical > 0 ) {
				velocidadInicial = velocidadInicial / Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
			} else {
				velocidadInicial = velocidadInicial / Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
			}
			dz = redondeaMultiplo( velocidadInicial * Math.sin(anguloVertical), Constantes.G );
			dz = redondeaMultiplo( dz + Constantes.G, Constantes.G );
			altura = altura - dz;
			
			PosicionCampo pAnterior = posicionInicial.moverAngulo( angulo, velocidadInicial );
			
			PosicionBalon posicionAnterior = new PosicionBalon( pAnterior, altura );
			
			posicionBalon.add( posicionAnterior );
		}
		
		initCache();
	}
	
	private void initCache() {
		cache.clear();
		if( posicionBalon.size() > 1 ) {
			cache.add( posicionBalon.get( 1 ) );
		} else {
			cache.add( posicionBalon.get( 0 ) );
		}
		cache.add( posicionBalon.get( 0 ) );
		getPosicionBalon( FutbolistaBase.TIEMPO_MAX_PASE );
	}
	
	
	
	public void nuevaPosicion( PosicionBalon posicion ) {
		if( posicionBalon.size() >= POSICIONES_ANTERIORES_MAX ) {
			posicionBalon.remove( posicionBalon.size() - 1 );
		}
		posicionBalon.add( 0, posicion );
		posicionRebote = null;
		posicionEnLineaFondo = null;
		initCache();
	}
	
	
	public PosicionBalon getPosicionBalon() {
		return posicionBalon.get( 0 );
	}
	
	/**
	 * Metodo que predice donde se encontrar� la pelota en un instante de tiempo futuro
	 * @param t Instante de tiempo futuro. Para t comprendida entre 1 y velocidadBalon
	 * @return
	 */
	public PosicionBalon getPosicionBalon( int t ) {
		t++;
		if( t < cache.size() ) {
			return cache.get( t );
			
		} else {
			double altura;
			double dx, dy, dz;
			double dx0, dy0, dz0;
			
			PosicionBalon pBalonActual = cache.get( cache.size()-1 );
			PosicionBalon pBalonPasado = cache.get( cache.size()-2 );
			
			altura = pBalonActual.getAltura();
			
			dx = pBalonActual.getX() - pBalonPasado.getX();
			dy = pBalonActual.getY() - pBalonPasado.getY();
			dz = (float) ( pBalonActual.getAltura() - pBalonPasado.getAltura() );
			
			PosicionBalon pBalon = new PosicionBalon( pBalonActual );
			boolean suelo = false;
			for( int i = cache.size(); i < t; i++ ) {
				dx0 = dx;
				dy0 = dy;
				dz0 = dz;
				
				if( !suelo && altura == 0 && dz >= 0 && dz < Constantes.G*3 ) {
					suelo = true;
				}
				if( suelo ) {
					dx = dx * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
					dy = dy * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
					if( posicionRebote == null ) posicionRebote = pBalon.moverPosicionBalon( dx, dy, altura );
				} else {
					dx = dx * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					dy = dy * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
					dz = redondeaMultiplo(dz - Constantes.G, Constantes.G);
					if( altura + dz < 0 ) {
						dz = (-dz - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
			            dz = redondeaMultiplo(dz, Constantes.G);
			            if( posicionRebote == null ) posicionRebote = pBalon.moverPosicionBalon( dx, dy, altura );
					}
					altura = redondeaMultiplo( altura + dz, Constantes.G);
				}
				pBalon = pBalon.moverPosicionBalon( dx, dy, altura );
				if( posicionEnLineaFondo == null && isEnLineaFondo(pBalon) ) {
					double posY =  -Constantes.LARGO_CAMPO_JUEGO / 2;
					if( pBalon.getY() > 0 ) {
						posY = Constantes.LARGO_CAMPO_JUEGO / 2;
					}
					double posZ = (dz0 / dy0) * (posY - pBalon.getY()) + altura;
					double posX = (dx0 / dy0) * (posY - pBalon.getY()) + pBalon.getX();
					posicionEnLineaFondo = new PosicionBalon( posX, posY, posZ );
				}
				cache.add( pBalon );
			}
			return pBalon;
		}
	}
	
	public PosicionCampo getPosicionRebote() {
		return posicionRebote;
	}
	
	public PosicionBalon getPosicionEnLineaFondo() {
		return posicionEnLineaFondo;
	}
	
	private double redondeaMultiplo(double valor, double divisor) {
        return Math.round(valor / divisor) * divisor;
    }
	
	public void resetear() {
		posicionBalon.clear();
		cache.clear();
	}
	
	
	
	public double getVelocidad() {
		return getPosicionBalon().distancia( getPosicionBalon( 1 ) );
	}
	
	public boolean enMovimiento() {
		if( getVelocidad() > 0.1 ) {
			return true;
		}
		return false;
	}
	
	private double corregirAngulo( double angulo ) {
		angulo = angulo % (Math.PI * 2d);
        if (angulo >= Math.PI) {
        	angulo = angulo - 2d * Math.PI;
        }
        return angulo;
		
	}
	
	private boolean isEnLineaFondo( PosicionBalon pBalon ) {
		return (Math.abs(pBalon.getY()) >= Constantes.LARGO_CAMPO_JUEGO / 2);
	}

	
}
